전체 포스트

객체 타입

객체 타입에 대해 살펴봅니다
2/21/2023 작성

개요

안녕하세요 이정환입니다 😃

이번에는 타입스크립트에서 객체 타입을 선언하고 사용하는 방법에 대해 살펴보겠습니다.

객체 타입을 선언하는 방법

자바스크립트에서 객체는 뗄레야 뗄 수 없을 정도로 자주 사용 되는 타입입니다. 타입스크립트에서는 객체의 타입을 어떻게 선언하고 활용하는지 자세히 살펴보겠습니다.

object 타입

타입스크립트에는 객체를 의미하는 타입인 object 타입이 존재합니다.

다음과 같이 변수에 객체 타입을 선언할 수 있습니다.

COPY
let obj: object = {
  name: "obj",
};

그러나 보통 타입스크립트에서 객체의 타입을 선언할 때 object는 잘 사용하지 않습니다. 그 이유는 아래의 예를 보면 알 수 있습니다.

COPY
let obj: object = {
  name: "obj",
};

obj.name;
// 오류 : object 타입에는 name 속성이 없습니다.

변수 obj의 타입이 객체임을 알리는데에는 성공했지만, obj에 저장된 객체의 프로퍼티에 접근하려고 하면 타입 오류가 발생합니다.

그 이유는 object 타입은 딱 ‘값 자체가 객체’라는 것만 알고 있기 때문입니다. 해당 객체가 어떤 프로퍼티를 갖고 또 value는 무슨 타입인지 알 수 없습니다.

객체 리터럴을 이용한 객체 타입 선언

그렇다면 어떻게 객체 타입을 선언해야 할까요? 다음과 같이 선언하면 됩니다.

COPY
let obj: { name: string } = {
  name: "obj",
};

obj.name;

obj의 타입을 { name : string } 으로 선언했습니다. 이렇게 마치 객체 리터럴을 사용하듯 객체에 담길 프로퍼티의 이름과 value의 타입을 직접 작성해 주면 타입 오류가 발생하지 않습니다.

이렇듯 객체의 타입을 프로퍼티와 value의 구조를 기반으로 선언하는 타입스크립트의 문법을 ‘객체 리터럴’ 이라고 합니다.

당연히 선언된 타입에 존재하지 않는 프로퍼티에 접근하려고 하면 오류가 발생합니다.

COPY
let obj: { name: string } = {
  name: "obj",
};

obj.c; // 오류 : { name : string}에는 c가 없습니다.

변수 obj의 타입으로 “string 타입의 값을 저장하는 name 프로퍼티를 갖는 객체”을 선언했기 때문에 존재하지 않는 c 프로퍼티에 접근하려고 하면 오류가 발생합니다.

이렇듯 객체 타입을 선언하여 존재하지 않는 프로퍼티에 잘못 접근하려는 경우를 방지할 수 있습니다. 참고로 자바스크립트에서는 이런 코드에도 그저 undefined이 반환될 뿐 어떠한 오류도 발생하지 않았습니다.

또 다음과 같이 타입 선언시에 알려주지 않은 프로퍼티를 추가하려고 해도 오류가 발생합니다.

COPY
let obj: { name: string } = {
  name: "obj",
};

obj = {
  name: "changed obj",
  extra: "extra property",
  // 오류 :
  // '{ name: string; extra: string; }' 형식은
  // '{ name: string; }' 형식에 할당할 수 없습니다.
};
COPY
let obj: { name: string } = {
  name: "obj",
};

obj.extra1 = "extra property";
// 오류 : '{ name: string; }' 형식에 'extra1' 속성이 없습니다.

obj["extra2"] = "extra property2";
// 오류 : '{ name: string; }' 형식에 'extra2' 속성이 없습니다.

앞서 이야기했듯 대부분의 상황에 타입스크립트는 변수의 타입을 잘 추론합니다. 따라서 다음과 같이 타입 추론을 타입스크립트에 위임 해도 됩니다.

COPY
let obj = {
  // { name : string } 으로 추론 됨
  name: "obj",
};

obj.name;

변수 obj의 타입을 잘 추론하는 걸 확인할 수 있습니다.

객체 배열 타입

자바스크립트에서는 보통 게시글이나 피드의 데이터를 객체 배열로 표현합니다. 여기서 말하는 객체 배열이란 여러개의 객체를 담는 배열을 뜻합니다.

예를들어 여러 회원들의 정보를 담는 배열이 필요하다면 다음과 같이 만들 수 있습니다.

COPY
let users = [
  { id: "1", name: "이정환" },
  { id: "2", name: "김아무개" },
  { id: "3", name: "이아무개" },
  { id: "4", name: "박아무개" },
];

그렇다면 이때 변수 users의 타입은 무엇으로 추론될까요? 앞선 내용(배열고 ㅏㅌ

마우스 커서를 올려 추론된 타입을 확인해보겠습니다

추론된 타입은 다음과 같습니다.

COPY
{ id : string, name : string }[]

이렇듯 객체 배열의 타입을 선언해야 할 때에는 객체 리터럴 [] 형식으로 타입을 선언해야 합니다. 숫자형 배열 타입을 선언하기 위해 number[] 처럼 타입을 선언하듯 객체 배열 타입을 선언할 때에는 객체 리터럴[] 로 타입을 선언합니다.

직접 타입을 선언해야 한다면 다음과 같이 하면 됩니다.

COPY
let users: { id: string; name: string } = [
  { id: "1", name: "이정환" },
  { id: "2", name: "김아무개" },
  { id: "3", name: "이아무개" },
  { id: "4", name: "박아무개" },
];

선택적 프로퍼티(Optional Property)

앞서 객체 배열 타입을 선언하는 방법에 대해 알아보았습니다. 그렇다면 이번에는 상황을 조금 바꿔 특정 유저들은 id만 존재하고 name은 존재하지 않는 경우를 가정하겠습니다.

COPY
let users: { id: string; name: string }[] = [
  { id: "1", name: "이정환" },
  { id: "2" }, // 오류
  { id: "3", name: "이아무개" },
  { id: "4" }, // 오류
];

id가 2인 유저와 4인 유저를 표현하는 두 객체는 name 프로퍼티를 갖지 않기 때문에 타입 오류가 발생합니다. 실제 프로덕트를 작성하다보면 이렇게 모든 객체가 동일한 프로퍼티를 갖지 않는 경우가 많습니다. 이런 경우에는 어떻게 타입을 선언해 주어야 할까요?

이럴 때에는 타입스크립트의 선택적 프로퍼티(Optional Property)를 이용하면 됩니다.

선택적 프로퍼티는 객체에서 있어도 되고 없어도 상관없는 프로퍼티라는 뜻 입니다. 말 뜻 그대로 선택적으로 이 프로퍼티를 포함하거나 제외할 수 있음을 의미합니다.

특정 프로퍼티를 선택적 프로퍼티로 만들기 위해서는 객체 리터럴에서 프로퍼티 이름 뒤에 ?를 붙이면 됩니다.

COPY
let users: { id: string; name?: string }[] = [
  { id: "1", name: "이정환" },
  { id: "2" },
  { id: "3", name: "이아무개" },
  { id: "4" },
];

name:stringname?:string으로 변경했습니다. 그 결과 name은 선택적 프로퍼티가 되어 오류가 사라집니다. users 배열에 저장된 객체 들은 id 프로퍼티는 필수로 가져야 하지만 name 프로퍼티는 있어도 되고 없어도 되기 때문입니다.

이렇듯 선택적 프로퍼티를 이용하면 특정 프로퍼티는 생략할 수 있도록 타입을 선언할 수 있습니다. 따라서 다양한 구조의 객체를 하나의 타입으로 표현할 수 있습니다. 실무에서도 매우 자주 사용되는 기본적인 기능이니 꼭 숙지하길 바랍니다.

💡 자바스크립트의 옵셔널 체이닝과 헷갈리면 안됩니다.
자바스크립트의 기능 중 특정 객체가 null이거나 undefined일 수 있는 상황에 ? 를 이용해 오류를 방지하는 기능이 있습니다. 이를 옵셔널 체이닝 이라고 부릅니다. 예를 들면 다음과 같습니다.

COPY
let user;
console.log(user.name);

// TypeError: Cannot read properties of undefined

이 자바스크립트 코드는 실행하면 오류가 발생합니다. 변수 name에는 undefine가 저장되어 있기 때문에 name에 마치 객체가 저장되어 있는 것 처럼 점 표기법을 사용하면 오류가 발생합니다.

이럴 때 옵셔널 체이닝을 이용하면 오류가 발생하지 않도록 할 수 있습니다.

COPY
let user;
console.log(user?.name);

// undefined

타입스크립트의 선택적 프로퍼티는 변수 이름에 ?를 붙이는게 아닌 타입으로 선언된 객체 리터럴에 붙입니다. 오해 없길 바랍니다.

COPY
let user: { name?: string } = {
  // ^^^ 선택적 프로퍼티
  name: "user",
};

console.log(user?.name);
// ^^^ 옵셔널 체이닝

자바스크립트의 기능 중 특정 객체가 null이거나 undefined일 수 있는 상황에 ? 를 이용해 오류를 방지하는 기능이 있습니다. 이를 옵셔널 체이닝 이라고 부릅니다. 예를 들면 다음과 같습니다.

COPY
let user;
console.log(user.name);

// TypeError: Cannot read properties of undefined

이 자바스크립트 코드는 실행하면 오류가 발생합니다. 변수 name에는 undefine가 저장되어 있기 때문에 name에 마치 객체가 저장되어 있는 것 처럼 점 표기법을 사용하면 오류가 발생합니다.

이럴 때 옵셔널 체이닝을 이용하면 오류가 발생하지 않도록 할 수 있습니다.

COPY
let user;
console.log(user?.name);

// undefined

타입스크립트의 선택적 프로퍼티는 변수 이름에 ?를 붙이는게 아닌 타입으로 선언된 객체 리터럴에 붙입니다. 오해 없길 바랍니다.

COPY
let user: { name?: string } = {
	// ^^^ 선택적 프로퍼티
  name: "user",
};

console.log(user?.name);
// ^^^ 옵셔널 체이닝

읽기 전용 프로퍼티(Readonly Property)

자바스크립트의 객체에 저장된 프로퍼티의 값(Value)은 자유롭게 수정할 수 있습니다. 따라서 당연히 타입스크립트의 객체 또한 타입 오류가 발생하지 않는 경우 프로퍼티의 값을 자유롭게 수정할 수 있습니다.

COPY
let user: { name: string } = {
  name: "user1",
};

user.name = "user2"; // 가능
user.name = 123; // 오류

user.name의 타입은 string이므로 “user2”로 변경하는 것은 가능하나 123으로 변경하는 것은 당연히 불가능합니다. 123은 number 타입인 반면 user.name은 string 타입으로 선언되었기 때문입니다.

가끔은 객체에서 절대 수정해서는 안되는 프로퍼티도 있을 것 입니다.

그러나 안타깝게도 자바스크립트에서는 객체 프로퍼티 값을 수정하지 못하도록 강제할 수 없습니다. const로 변수를 선언하고 객체를 저장해도 객체 프로퍼티의 값을 바꾸는 동작은 허용됩니다.(물론 writable 이나 freeze 등의 기능을 활용해 어떻게든 막을수는 있지만 복잡하고 어렵습니다)

COPY
const user = {
  name: "이정환",
};

user.name = "이정환 아니다";

console.log(user.name);
// 이정환 아니다

그런데 타입스크립트에서는 아주 간단하게 특정 프로퍼티의 값을 수정하지 못하도록 할 수 있습니다. 객체 리터럴에서 특정 프로퍼티의 이름 앞에 readonly 키워드를 붙여 읽기 전용 프로퍼티로 만들면 됩니다. 읽기 전용 프로퍼티란 오직 값을 읽는 것(가져오는 것)만 가능하고 수정할 수는 없는 프로퍼티를 뜻 하빈다.

COPY
const user: { readonly name: string } = {
  name: "이정환",
};

user.name = "이정환 아니다";
// 오류 : 읽기 전용 속성이므로 name에 할당할 수 없습니다 ...

name : stringreadonly name : string으로 변경했습니다. 그 결과 name 프로퍼티를 수정하려고 하니 타입 오류가 발생합니다. 이렇듯 읽기 전용 프로퍼티를 이용하면 객체에서 특정 프로퍼티의 값을 수정하지 못하도록 제한할 수 있습니다.